package cn.yto.print.listener;

import cn.yto.print.config.CaffeineCacheConfig;
import cn.yto.print.config.ThreadPoolManager;
import cn.yto.print.constants.AssistantConstants;
import cn.yto.print.enums.PluginRegistryEnum;
import cn.yto.print.plugins.PluginStatusManager;
import cn.yto.print.utils.CommonUtil;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.Tlhelp32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinNT;
import com.sun.jna.win32.W32APIOptions;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class PluginProcessMonitor implements AutoCloseable {

    private static volatile PluginProcessMonitor instance;
    private final Kernel32 kernel32Ref;
    private final ScheduledExecutorService schedulerService;
    private final Map<String, Set<ProcessListener>> listenerMap;
    private final Map<String, Long> intervalMap;

    // 当前监控是否处于活动状态
    private boolean isMonitoring;

    private PluginProcessMonitor() {
        this.kernel32Ref = Native.loadLibrary(Kernel32.class, W32APIOptions.UNICODE_OPTIONS);
        this.schedulerService = ThreadPoolManager.getSingleScheduledExecutor();
        this.listenerMap = new ConcurrentHashMap<>();
        this.intervalMap = new ConcurrentHashMap<>();
        this.isMonitoring = false;
        if (instance != null) {
            throw new IllegalStateException("PluginProcessMonitor singleton instance already exists");
        }
    }

    public static PluginProcessMonitor getInstance() {
        if (instance == null) {
            synchronized (PluginProcessMonitor.class) {
                if (instance == null) {
                    instance = new PluginProcessMonitor();
                }
            }
        }
        return instance;
    }

    /**
     * 判断某个进程是否正在运行
     */
    public boolean isProcessRunning(String processName) {
        Tlhelp32.PROCESSENTRY32.ByReference entry = new Tlhelp32.PROCESSENTRY32.ByReference();
        WinNT.HANDLE snapshot = kernel32Ref.CreateToolhelp32Snapshot(Tlhelp32.TH32CS_SNAPPROCESS, new WinDef.DWORD(0));
        if (snapshot == null) {
            return false;
        }
        try {
            // 如果无法获取第一个进程，则返回false
            if (!kernel32Ref.Process32First(snapshot, entry)) {
                return false;
            }
            do {
                String exeName = Native.toString(entry.szExeFile);
                if (exeName.contains(processName.replaceAll("\\.exe$", ""))) {
                    return true;
                }
            } while (kernel32Ref.Process32Next(snapshot, entry));
            return false;
        } finally {
            kernel32Ref.CloseHandle(snapshot);
        }
    }

    public void addProcessListener(String processName, ProcessListener listener) {
        listenerMap.computeIfAbsent(processName, k -> ConcurrentHashMap.newKeySet()).add(listener);
        intervalMap.putIfAbsent(processName, 5000L);
        if (!isMonitoring) {
            startMonitoring();
        }
    }

    public void addProcessListener(String processName, ProcessListener listener, long checkInterval) {
        intervalMap.put(processName, checkInterval);
        addProcessListener(processName, listener);
    }

    /**
     * 启动监控
     */
    private synchronized void startMonitoring() {
        if (isMonitoring) {
            return;
        }
        isMonitoring = true;
        schedulerService.scheduleAtFixedRate(this::checkAllProcesses, 0, 1, TimeUnit.SECONDS);
    }

    public void removeProcessListener(String processName, ProcessListener listener) {
        listenerMap.computeIfPresent(processName, (k, v) -> {
            v.remove(listener);
            return v.isEmpty() ? null : v;
        });
        if (listenerMap.isEmpty()) {
            close();
        }
    }

    private void checkAllProcesses() {
        long now = System.currentTimeMillis();
        intervalMap.forEach((processName, interval) -> {
            if (now % interval < 1000) {
                checkProcessByName(processName);
            }
        });
    }

    private void checkProcessByName(String processName) {
        // 通过进程名找到对应插件
        PluginRegistryEnum pluginRegistry = PluginRegistryEnum.getPluginByProcesses(processName);
        if (pluginRegistry == null || CaffeineCacheConfig.getProcessCache().asMap().containsKey(pluginRegistry.getCode())) {
            return;
        }
        Integer oldStatus = PluginStatusManager.getInstance().getPluginStatus().get(pluginRegistry.getCode());
        boolean wasRunning = oldStatus == AssistantConstants.OPENED;
        boolean isRunning = isProcessRunning(processName);
        if (isRunning != wasRunning) {
            CaffeineCacheConfig.getProcessCache().put(pluginRegistry.getCode(), isRunning);
            listenerMap.getOrDefault(processName, Collections.emptySet()).forEach(listener -> {
                if (isRunning) {
                    listener.onProcessStarted(processName);
                } else {
                    listener.onProcessStopped(processName);
                }
            });
        }
    }

    @Override
    public synchronized void close() {
        if (!isMonitoring) {
            return;
        }
        isMonitoring = false;
        CommonUtil.gracefulShutdown(schedulerService);
    }

}



